{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 双线性插值知识点补充\n",
    "\n",
    "1. 双线性插值简单实现\n",
    "2. 实现初始化反卷积核"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import cv2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 双线性插值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def Bilinear_interpolation (src, new_size):\n",
    "    \"\"\"使用双线性插值方法放大图像\n",
    "    para:\n",
    "        src(np.ndarray):输入图像\n",
    "        new_size:(tuple): 目标尺寸\n",
    "    ret:\n",
    "        dst(np.ndarray): 目标图像\n",
    "    \"\"\"\n",
    "    dst_h, dst_w = new_size # 目标图像的高和宽\n",
    "    src_h, src_w = src.shape[:2]  #源图像的高和宽\n",
    "    \n",
    "    if src_h == dst_h and src_w == dst_w: # 未操作，只拷贝原始图片\n",
    "        return src.copy()\n",
    "    # 若不一样则进行缩放\n",
    "    scale_x = float(src_w) / dst_w   #缩放比例\n",
    "    scale_y = float(src_h) / dst_h\n",
    "    \n",
    "    # 插值计算\n",
    "    #遍历目标图上的每一个像素，由原图的点插入数值\n",
    "    dst = np.zeros((dst_h, dst_w, 3), dtype=np.uint8) #生成一张目标尺寸大小的空白图，遍历插值\n",
    "    for n in range(3): #循环channel\n",
    "        for dst_y in range(dst_h): #循环height\n",
    "            for dst_x in range(dst_w): #循环width\n",
    "                #目标像素在源图上的坐标\n",
    "                # src_x + 0.5 = (dst_x + 0.5) * scale_x \n",
    "                # 根据上面公式写的代码\n",
    "                src_x = (dst_x + 0.5) * scale_x - 0.5  # 一个像素默认为1*1的小格子，其中心在像素坐标点加0.5的位置\n",
    "                src_y = (dst_y + 0.5) * scale_y - 0.5\n",
    "                \n",
    "                # 缩小的过程中会避免原始图像像素点不会被计算到，所以加0.5\n",
    "                # -0.5：避免没被\n",
    "                \n",
    "                #计算在源图上四个近邻点的位置\n",
    "                src_x_0 = int(np.floor(src_x))  #向下取整 floor(1.2) = 1.0\n",
    "                src_y_0 = int(np.floor(src_y))\n",
    "                src_x_1 = min(src_x_0 + 1, src_w - 1)  #防止出界\n",
    "                src_y_1 = min(src_y_0 + 1, src_h - 1)\n",
    "                \n",
    "                #双线性插值 新图像每个像素的值来自于原图像上像素点的组合插值\n",
    "                value0 = (src_x_1 - src_x) * src[src_y_0, src_x_0, n] + (src_x - src_x_0) * src[src_y_0, src_x_1, n]\n",
    "                value1 = (src_x_1 - src_x) * src[src_y_1, src_x_0, n] + (src_x - src_x_0) * src[src_y_1, src_x_1, n]\n",
    "                dst[dst_y, dst_x, n] = int((src_y_1 - src_y) * value0 + (src_y - src_y_0) * value1)\n",
    "                \n",
    "    return dst\n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if __name__ == '__main__':\n",
    "    img_in = cv2.imread('dog.jpg')\n",
    "    img_out = Bilinear_interpolation(img_in, (1000,1000))\n",
    "    cv2.imshow('src_img',img_in)\n",
    "    cv2.imshow('dst_img',img_out)\n",
    "    \n",
    "    key = cv2.waitKey()\n",
    "    if key == 27:\n",
    "        cv2.destroyAllWindows()\n",
    "        \n",
    "    print(img_in.shape)\n",
    "    print(img_out.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 手动初始化转置卷积的卷积核"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "og = np.ogrid[:4, :4]\n",
    "print(og[0])\n",
    "print(og[1])\n",
    "filt = (1 - abs(og[0] - 2) / 2) * (1 - abs(og[1] - 2) / 2)\n",
    "print(filt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 手动设计一个滤子\n",
    "def bilinear_kernel(in_channels, out_channels, kernel_size):\n",
    "    \"\"\"使用双线性插值的方法初始化卷积层中卷积核的权重参数\n",
    "    para:\n",
    "        in_channels(int): 输入通道数\n",
    "        out_channels(int): 输出通道数\n",
    "        kernel_size()\n",
    "    ret:\n",
    "        torch.tensor : a bilinear filter kernel\n",
    "    \"\"\"\n",
    "    \n",
    "    factor = (kernel_size + 1) // 2\n",
    "    center = kernel_size/2\n",
    "    # 构建网格\n",
    "    og = np.ogrid[:kernel_size, :kernel_size]\n",
    "    # 填充\n",
    "    filt = (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)\n",
    "    weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size), dtype='float32')\n",
    "    weight[range(in_channels), range(out_channels), :, :] = filt\n",
    "    \n",
    "    return torch.from_numpy(weight)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试\n",
    "x = plt.imread(\"dog.jpg\")\n",
    "print(x.shape)\n",
    "x = torch.from_numpy(x.astype('float32')).permute(2, 0, 1).unsqueeze(0)  # .unsqueeze(0) 增加 batch_size 通道,没有则会报错\n",
    "conv_trans = nn.ConvTranspose2d(3, 3, 4, 2, 1) # 3*3 输入输出通道，4是卷积核大小，2是步长，1是padding\n",
    "# 将其定义为 bilinear kernel\n",
    "conv_trans.weight.data = bilinear_kernel(3, 3, 4)\n",
    "# print(conv_trans.weight)\n",
    "# print(conv_trans.weight.data)\n",
    "y = conv_trans(x).data.squeeze().permute(1, 2, 0).numpy()\n",
    "plt.imshow(y.astype('uint8'))\n",
    "print(y.shape)\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
